// Copyright (c) 2010 Manuel Peinado Gallego <manuel.peinado@gmail.com>
// Distributed under the MIT license


#include "stdafx.h"
#include "AABB.h"
#include "Triangle.h"
#include "Ray.h"
#include "Quad.h"

void getAABBTriangles(int face, Triangle dest[2])
{
    gx::Vec3 pnts[] = { 
        gx::Vec3(1,0,1), gx::Vec3(1,0,0),
        gx::Vec3(0,0,0), gx::Vec3(0,0,1),
        gx::Vec3(1,1,1), gx::Vec3(1,1,0),
        gx::Vec3(0,1,0), gx::Vec3(0,1,1) 
    };

    for(int i = 0; i < 8; ++i)
    {
        pnts[i] = pnts[i] - .5 * gx::Vec3(1., 1., 1.);
    }
        
    switch(face)
    {
    case 0: 
        dest[0] = Triangle(pnts[3],pnts[0],pnts[4]);
        dest[1] = Triangle(pnts[3],pnts[4],pnts[7]);
        break;
    case 1: 
        dest[0] = Triangle(pnts[0],pnts[1],pnts[5]);
        dest[1] = Triangle(pnts[0],pnts[5],pnts[4]);
        break;
    case 2: 
        dest[0] = Triangle(pnts[1],pnts[2],pnts[6]);
        dest[1] = Triangle(pnts[1],pnts[6],pnts[5]);
        break;
    case 3: 
        dest[0] = Triangle(pnts[2],pnts[3],pnts[7]);
        dest[1] = Triangle(pnts[2],pnts[7],pnts[6]);
        break;
    case 4: 
        dest[0] = Triangle(pnts[4],pnts[5],pnts[6]);
        dest[1] = Triangle(pnts[4],pnts[6],pnts[7]);
        break;
    case 5: 
        dest[0] = Triangle(pnts[1],pnts[0],pnts[3]);
        dest[1] = Triangle(pnts[1],pnts[3],pnts[2]);
        break;
    default:
        ASSERT(0);
    }
}

Quad getAABBFaceQuad(int face)
{
    Triangle tris[2];
    getAABBTriangles(face, tris);
    return Quad(tris[0].v0, tris[0].v1, 
                tris[0].v2, tris[1].v2);
}

void getAllAabbFaces(Quad dest[6])
{
    for(int i = 0; i < 6; ++i)
    {
        dest[i] = getAABBFaceQuad(i);
    }
}

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

AABB::AABB()
{
}

AABB::AABB(const gx::Vec3 & d, const gx::Vec3 & c)
: dims(d), center(c)
{
}

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

AABB& AABB::fromCorners(const gx::Vec3& minCorner, const gx::Vec3& maxCorner)
{
    dims = maxCorner - minCorner;
    center = .5 * (maxCorner + minCorner);
    return *this;
}

void AABB::getCorners(gx::Vec3& minCorner, gx::Vec3& maxCorner) const
{
    gx::Vec3 halfDims = dims * .5;
    minCorner = center - halfDims;
    maxCorner = center + halfDims;
}

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

void AABB::combine(const AABB& other)
{
    gx::Vec3 minCorner, maxCorner, otherMin, otherMax;
    getCorners(minCorner, maxCorner);
    other.getCorners(otherMin, otherMax);

    minCorner.x = std::min(minCorner.x, otherMin.x);
    minCorner.y = std::min(minCorner.y, otherMin.y);
    minCorner.z = std::min(minCorner.z, otherMin.z);

    maxCorner.x = std::max(maxCorner.x, otherMax.x);
    maxCorner.y = std::max(maxCorner.y, otherMax.y);
    maxCorner.z = std::max(maxCorner.z, otherMax.z);

    fromCorners(minCorner, maxCorner);
}

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

bool AABB::rayIntersection(const Ray & ray, double & dist2) const
{
    int result = -1;
    Quad faces[6];
    getAllAabbFaces(faces);
    for(int i = 0; i < 6; ++i)
    {
        Triangle faceTris[2];
        getAABBTriangles(i, faceTris);
        for(int triIdx = 0; triIdx < 2; ++triIdx)
        {
            faceTris[triIdx].v0.x = faceTris[triIdx].v0.x * dims.x + center.x;
            faceTris[triIdx].v0.y = faceTris[triIdx].v0.y * dims.x + center.y;
            faceTris[triIdx].v0.z = faceTris[triIdx].v0.z * dims.x + center.z;

            faceTris[triIdx].v1.x = faceTris[triIdx].v1.x * dims.x + center.x;
            faceTris[triIdx].v1.y = faceTris[triIdx].v1.y * dims.x + center.y;
            faceTris[triIdx].v1.z = faceTris[triIdx].v1.z * dims.x + center.z;

            faceTris[triIdx].v2.x = faceTris[triIdx].v2.x * dims.x + center.x;
            faceTris[triIdx].v2.y = faceTris[triIdx].v2.y * dims.x + center.y;
            faceTris[triIdx].v2.z = faceTris[triIdx].v2.z * dims.x + center.z;

            gx::Vec3 interPnt;
            result = faceTris[triIdx].rayIntersection(ray, interPnt);
            if(result > 0)
            {
                dist2 = interPnt.dist2(ray.origin);
                result = i;
                goto END;
            }
        }
    }
END:
    // Translate face index to the convention our app understands

    if(result == -1)
    {
        return false;
    }
//    int faceMap[] = { 3, 1, -3, -1, 2, -2 };
    return true;//faceMap[result];
}
